/*
 * MOAB, a Mesh-Oriented datABase, is a software component for creating,
 * storing and accessing finite element mesh data.
 *
 * Copyright 2004 Sandia Corporation.  Under the terms of Contract
 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
 * retains certain rights in this software.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 */

/**\file RangeSeqIntersectIter.hpp
 *\author Jason Kraftcheck (kraftche@cae.wisc.edu)
 *\date 2006-08-11
 *\date 2007-11-06
 */

#ifndef MB_RANGE_SEQ_INTERSECT_ITER_HPP
#define MB_RANGE_SEQ_INTERSECT_ITER_HPP

#include "moab/Types.hpp"
#include "moab/Range.hpp"

namespace moab
{

class SequenceManager;
class EntitySequence;

#define MB_RANGE_SEQ_INTERSECT_ITER_STATS 0

/** \brief Iterate over the blocks of EntityHandles in an Range that
 *         are in the same EntitySequence.
 *
 * Iterate over an Range, returning blocks of entities that are the
 * largest ranges of contiguous handles that meet one of the following
 * conditions:
 *  - All are valid handles belonging to the same EntitySequence
 *  - All are invalid handles of the same EntityType
 *
 * The return type from init() or step() indicates whether or not the
 * current range contains valid entities.  If the handles are either
 * all valid or all holes in an EntitySequence, that sequence can
 * be obtained with get_sequence().  get_sequence() will return NULL if
 * there is no corresponding EntitySequence for the block of handles.
 *
 * This class keeps a data related to the 'current' EntitySequence and
 * references to the Range pasesd to init().  Changing either of these
 * while an instance of this class is in use would be bad.
 */
class RangeSeqIntersectIter
{
  public:
    RangeSeqIntersectIter( SequenceManager* sequences )
        : mSequenceManager( sequences ), mSequence( 0 ), mStartHandle( 0 ), mEndHandle( 0 ), mLastHandle( 0 )
    {
    }

    /** Initialize iterator to first valid subset
     *\return - MB_SUCCESS : initial position of iterator is valid
     *        - MB_ENITITY_NOT_FOUND : range contains invalid handle -- can step past by calling
     *again
     *        - MB_FAILURE : No entities (start == end)
     */
    ErrorCode init( Range::const_iterator start, Range::const_iterator end );

    /** Step iterator to next range.
     *\return - MB_SUCCESS : there is another range, and iter has been changed to it
     *        - MB_ENITITY_NOT_FOUND : range contains invalid handle -- can step past by calling
     *again
     *        - MB_FAILURE : at end.
     */
    ErrorCode step();

    /**\brief Check if next call to step() will return MB_FAILURE.
     *
     * Check if the iterator cannot be advanced any further.
     * If this method returns true, then the *next* call to step()
     * will return MB_FAILURE.
     */
    bool is_at_end() const
    {
        return mEndHandle == mLastHandle;
    }

    /** Get the EntitySequence for the current block.
     *  May be NULL for invaild handles.
     */
    EntitySequence* get_sequence() const
    {
        return mSequence;
    }

    /** Get first handle in block */
    EntityHandle get_start_handle() const
    {
        return mStartHandle;
    }

    /** Get last handle in block */
    EntityHandle get_end_handle() const
    {
        return mEndHandle;
    }

#if MB_RANGE_SEQ_INTERSECT_ITER_STATS
    static double fragmentation()
    {
        return ( doubleNumCalls + intNumCalls ) / ( doubleEntCount + intEntCount );
    }
#endif

  private:
    /** Update entity sequence data (mSequence and freeIndex) for current
     *  mStartHandle.  If mEndHandle is past end of sequence, trim it.
     *  Called by step() and init().  step() handles iterating over the pairs
     *  in the Range.  This method handles iterating over the set of
     *  EntitySequences that intersect the pair.
     */
    ErrorCode update_entity_sequence();

    /** Handle error case where we encountered an EntityHandle w/out
     *  a corresponding EntitySequence.  Trim mEndHandle such that it
     *  is before the next valid EntityHandle of the same type.
     *\return Always returns MB_ENTITY_NOT_FOUND
     */
    ErrorCode find_invalid_range();

    SequenceManager* mSequenceManager;      //!< THE EntitySequenceManager
    EntitySequence* mSequence;              //!< EntitySequence corresponding to current location
    Range::const_pair_iterator rangeIter;   //!< Current position in Range.
    EntityHandle mStartHandle, mEndHandle;  //!< Subset of current EntitySequence
    EntityHandle mLastHandle;               //!< The last of the list of all handles in the Range

#if MB_RANGE_SEQ_INTERSECT_ITER_STATS
    static double doubleNumCalls, doubleEntCount;
    static unsigned long intNumCalls, intEntCount;
    static void update_stats( unsigned long num_ents );
#endif
};

}  // namespace moab

#endif
